package bg.btanev.eightyfour.gwtfacade;

import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import bg.btanev.eightyfour.client.service.GWTCommandService;
import bg.btanev.eightyfour.domain.Tag;
import bg.btanev.eightyfour.domain.Target;
import bg.btanev.eightyfour.domain.entitybase.DomainEntity;
import bg.btanev.eightyfour.domain.geo.GeoLocation;
import bg.btanev.eightyfour.domain.person.Address;
import bg.btanev.eightyfour.domain.person.ContactInfo;
import bg.btanev.eightyfour.domain.person.Person;
import bg.btanev.eightyfour.domain.person.PersonName;
import bg.btanev.eightyfour.domain.profile.Profile;
import bg.btanev.eightyfour.domain.profile.ProfileEvent;
import bg.btanev.eightyfour.domain.profile.feed.FeedMediaContent;
import bg.btanev.eightyfour.domain.profile.feed.FeedMediaContentDimensions;
import bg.btanev.eightyfour.domain.profile.feed.FeedMediaContentMeta;
import bg.btanev.eightyfour.domain.profile.feed.FeedProfile;
import bg.btanev.eightyfour.domain.profile.feed.FeedProfileEvent;
import bg.btanev.eightyfour.domain.target.TargetStatus;
import bg.btanev.eightyfour.domain.target.TargetStatusLog;
import bg.btanev.eightyfour.gwtfacade.commandhandler.CommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.CreateTargetCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.DeleteProfileCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.FindPersonCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.FindProfileEventCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.FindProfilesCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.FindTagsForTargetCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.FindTargetCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.SaveProfileCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.StatDayActivityCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.StatHourActivityCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.StatWeekDayActivityCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.StatsSummaryCommandHandler;
import bg.btanev.eightyfour.gwtfacade.commandhandler.UpdatePersonCommandHandler;
import bg.btanev.eightyfour.gwtfacade.converter.ConverterConfiguration;
import bg.btanev.eightyfour.gwtfacade.converter.ConverterFactory;
import bg.btanev.eightyfour.gwtfacade.converter.DtoConverter;
import bg.btanev.eightyfour.service.AnalyticsService;
import bg.btanev.eightyfour.service.PersonService;
import bg.btanev.eightyfour.service.ProfileService;
import bg.btanev.eightyfour.service.TargetService;
import bg.btanev.eightyfour.shared.command.CommandAction;
import bg.btanev.eightyfour.shared.command.CommandInput;
import bg.btanev.eightyfour.shared.command.CommandResult;
import bg.btanev.eightyfour.shared.command.person.FindPersonCommand;
import bg.btanev.eightyfour.shared.command.person.UpdatePersonCommand;
import bg.btanev.eightyfour.shared.command.profile.DeleteProfileCommand;
import bg.btanev.eightyfour.shared.command.profile.FindProfilesCommand;
import bg.btanev.eightyfour.shared.command.profile.SaveProfileCommand;
import bg.btanev.eightyfour.shared.command.profileevent.FindProfileEventsCommand;
import bg.btanev.eightyfour.shared.command.stat.StatDayActivityCommand;
import bg.btanev.eightyfour.shared.command.stat.StatHourActivityCommand;
import bg.btanev.eightyfour.shared.command.stat.StatSummaryCommand;
import bg.btanev.eightyfour.shared.command.stat.StatWeekDayActivityCommand;
import bg.btanev.eightyfour.shared.command.target.CreateTargetCommand;
import bg.btanev.eightyfour.shared.command.target.FindTagsForTargetCommand;
import bg.btanev.eightyfour.shared.command.target.FindTargetsCommand;
import bg.btanev.eightyfour.shared.dto.TagDto;
import bg.btanev.eightyfour.shared.dto.TargetDto;
import bg.btanev.eightyfour.shared.dto.dtobase.DomainDtoEntity;
import bg.btanev.eightyfour.shared.dto.geo.GeoLocationDto;
import bg.btanev.eightyfour.shared.dto.person.AddressDto;
import bg.btanev.eightyfour.shared.dto.person.ContactInfoDto;
import bg.btanev.eightyfour.shared.dto.person.PersonDto;
import bg.btanev.eightyfour.shared.dto.person.PersonNameDto;
import bg.btanev.eightyfour.shared.dto.profile.ProfileDto;
import bg.btanev.eightyfour.shared.dto.profile.ProfileEventDto;
import bg.btanev.eightyfour.shared.dto.profile.feed.FeedMediaContentDimensionsDto;
import bg.btanev.eightyfour.shared.dto.profile.feed.FeedMediaContentDto;
import bg.btanev.eightyfour.shared.dto.profile.feed.FeedMediaContentMetaDto;
import bg.btanev.eightyfour.shared.dto.profile.feed.FeedProfileDto;
import bg.btanev.eightyfour.shared.dto.profile.feed.FeedProfileEventDto;
import bg.btanev.eightyfour.shared.dto.target.TargetStatusDto;
import bg.btanev.eightyfour.shared.dto.target.TargetStatusLogDto;

/**
 *
 * @author B.Tanev
 *
 */
public class GWTCommandServiceImpl implements GWTCommandService {
	/**
	 *
	 */
	private static final long serialVersionUID = 4000908241515347063L;

	private PersonService personService;
	private TargetService targetService;
	private ProfileService profileService;
	private AnalyticsService analyticsService;

	private DtoConverter converter;

	private final Map<Class<CommandAction<?, ?>>, CommandHandler<?, ?, ?, ?>> handlers = new ConcurrentHashMap<Class<CommandAction<?, ?>>, CommandHandler<?, ?, ?, ?>>();

	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}

	public void setTargetService(TargetService targetService) {
		this.targetService = targetService;
	}

	public void setProfileService(ProfileService profileService) {
		this.profileService = profileService;
	}

	public void setAnalyticsService(AnalyticsService analyticsService) {
		this.analyticsService = analyticsService;
	}

	public void init() {
		ConverterFactory converterFactory = new ConverterFactory();
		ConverterConfiguration config = new ConverterConfiguration();

		config.addAssiciation(DomainEntity.class, DomainDtoEntity.class, true);
		config.addAssiciation(Tag.class, TagDto.class, true);
		// target
		config.addAssiciation(Target.class, TargetDto.class, true);
		config.addAssiciation(TargetStatus.class, TargetStatusDto.class, true);
		config.addAssiciation(TargetStatusLog.class, TargetStatusLogDto.class, true);
		// person
		config.addAssiciation(Person.class, PersonDto.class, true);
		config.addAssiciation(PersonName.class, PersonNameDto.class, true);
		config.addAssiciation(ContactInfo.class, ContactInfoDto.class, true);
		config.addAssiciation(Address.class, AddressDto.class, true);
		config.addAssiciation(GeoLocation.class, GeoLocationDto.class, true);
		// profile
		config.addAssiciation(Profile.class, ProfileDto.class, true);
		config.addAssiciation(FeedProfile.class, FeedProfileDto.class, true);
		// profile event
		config.addAssiciation(ProfileEvent.class, ProfileEventDto.class, true);
		config.addAssiciation(FeedProfileEvent.class, FeedProfileEventDto.class, true);
		config.addAssiciation(FeedMediaContent.class, FeedMediaContentDto.class, true);
		config.addAssiciation(FeedMediaContentMeta.class, FeedMediaContentMetaDto.class, true);
		config.addAssiciation(FeedMediaContentDimensions.class, FeedMediaContentDimensionsDto.class, true);
		// TODO add more

		converterFactory.setConfiguration(config);
		converter = converterFactory.getConverter();
	}

	@Override
	public <I extends CommandInput, R extends Serializable, CR extends CommandResult<I, R>, CA extends CommandAction<I, CR>> CR executeCommand(
			CA commandAction) {

		CommandHandler<I, R, CR, CA> commandHandler = determineCommandHandler(commandAction);
		CR commandResult = commandHandler.execute(commandAction);

		return commandResult;
	}

	@SuppressWarnings("unchecked")
	private <I extends CommandInput, R extends Serializable, CR extends CommandResult<I, R>, CA extends CommandAction<I, CR>> CommandHandler<I, R, CR, CA> determineCommandHandler(
			CA commandAction) {

		CommandHandler<I, R, CR, CA> commandHandler = (CommandHandler<I, R, CR, CA>) handlers.get(commandAction
				.getClass());
		if (commandHandler == null) {
			if (commandAction instanceof FindTargetsCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new FindTargetCommandHandler(targetService, converter);
			} else if (commandAction instanceof FindProfileEventsCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new FindProfileEventCommandHandler(profileService,
						targetService, converter);
			} else if (commandAction instanceof FindPersonCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new FindPersonCommandHandler(personService,
						targetService, converter);
			} else if (commandAction instanceof UpdatePersonCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new UpdatePersonCommandHandler(personService, converter);
			} else if (commandAction instanceof FindProfilesCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new FindProfilesCommandHandler(targetService,
						profileService, converter);
			} else if (commandAction instanceof SaveProfileCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new SaveProfileCommandHandler(targetService,
						profileService, converter);
			} else if (commandAction instanceof DeleteProfileCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new DeleteProfileCommandHandler(targetService,
						profileService, converter);
			} else if (commandAction instanceof FindTagsForTargetCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new FindTagsForTargetCommandHandler(targetService,
						converter);
			} else if (commandAction instanceof StatSummaryCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new StatsSummaryCommandHandler(targetService,
						profileService, converter);
			} else if (commandAction instanceof StatHourActivityCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new StatHourActivityCommandHandler(targetService,
						analyticsService);
			} else if (commandAction instanceof StatDayActivityCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new StatDayActivityCommandHandler(targetService,
						analyticsService);
			} else if (commandAction instanceof StatWeekDayActivityCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new StatWeekDayActivityCommandHandler(targetService,
						analyticsService);
			} else if (commandAction instanceof CreateTargetCommand) {
				commandHandler = (CommandHandler<I, R, CR, CA>) new CreateTargetCommandHandler(targetService, converter);
			}

			if (commandHandler != null) {
				handlers.put((Class<CommandAction<?, ?>>) commandAction.getClass(), (CommandHandler) commandHandler);
			}
		}

		assert (commandHandler != null);
		return commandHandler;
	}
}
